From db60c4bb335b6b77f2cb8beb3fb5fb7b4a2cebc3 Mon Sep 17 00:00:00 2001 From: robertl Date: Fri, 29 Jul 2005 17:11:22 +0000 Subject: [PATCH] Olaf adds writing to GDB. --- gpsbabel/README | 18 +- gpsbabel/gdb.c | 1463 +++++++++++++++++++++++------ gpsbabel/reference/gdb-sample.gpx | 426 +++++++++ gpsbabel/testo | 8 +- 4 files changed, 1604 insertions(+), 311 deletions(-) diff --git a/gpsbabel/README b/gpsbabel/README index dd6a205ce..c18bf5ac8 100644 --- a/gpsbabel/README +++ b/gpsbabel/README @@ -873,10 +873,20 @@ THE FORMATS GDB - Support for the "Garmin GPS Database" format used by default - in MapSource versions 6.1 and later. With this first step you - should be able to read waypoints, routes and tracks from .gdb - files. + Support for the "Garmin GPS Database" format used by default in + MapSource versions since release 6.0. By default we create gdb's + of version 2. Version 2 is used in Mapsource 6.3 and 6.5. + + Garmin GPS database is an undocumented file format. The + basic info for this module comes from the existing MapSource + conversion code. + + Additional options: + + ver - set the data format version of the output file + (currently 1 or 2); 2 is our default. + via - Drop hidden route points (means calculated stuff) + cat - default category on output (1..16) BCR diff --git a/gpsbabel/gdb.c b/gpsbabel/gdb.c index b5eb8cf95..a0ccebe2c 100644 --- a/gpsbabel/gdb.c +++ b/gpsbabel/gdb.c @@ -1,5 +1,5 @@ /* - Garmin GPS Database Reader + Garmin GPS Database Reader/Writer Copyright (C) 2005 Olaf Klein, o.b.klein@t-online.de Mainly based on mapsource.c, @@ -21,6 +21,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA */ +/* + History: + + 2005/06/27: initial release (reader only) + 2005/07/26: added write support + 2005/07/27: replaced "tricky code" in route reader + 2005/07/28: fixed handling of single point routes + new option "via" + new option "ver" + fixed compiler warnings +*/ + #include #include #include @@ -31,9 +43,17 @@ #define MYNAME "gdb" -#define MPSNAMEBUFFERLEN 1024 -#define MPSNOTESBUFFERLEN 4096 -#define MPSDESCBUFFERLEN 4096 +#undef GDB_DEBUG + +#define GDB_VER_MIN 1 +#define GDB_VER_MAX 2 + +#define GDB_DEFAULTWPTCLASS 0 +#define GDB_HIDDENROUTEWPTCLASS 8 + +#define GDB_NAME_BUFFERLEN 1024 +#define GDB_NOTES_BUFFERLEN 4096 +#define GDB_DESCR_BUFFERLEN 4096 #define DEFAULTICONVALUE 18 #define DEFAULTICONDESCR "Waypoint" @@ -46,14 +66,50 @@ /* %%% local vars %%% */ -FILE *fin; -static char *fin_name; +FILE *fin, *fout; +static char *fin_name, *fout_name; + static int gdb_ver = 1; static int gdb_debug = 0; -route_head *gdb_hidden; +static int gdb_via; /* 0 = read and write hidden points too; 1 = drop */ +static int gdb_category; + +route_head *gdb_hidden = NULL; + + +#define GDB_OPT_VER "ver" +#define GDB_OPT_VIA "via" +#define GDB_OPT_CATEGORY "cat" + +static char *gdb_opt_category = NULL; +static char *gdb_opt_ver = NULL; +static char *gdb_opt_via = NULL; + +static arglist_t gdb_args[] = { + {GDB_OPT_CATEGORY, &gdb_opt_category, + "Default category on output (1..16)", NULL, ARGTYPE_INT}, + {GDB_OPT_VER, &gdb_opt_ver, + "Version of gdb file to generate (1,2)", "2", ARGTYPE_INT}, + {GDB_OPT_VIA, &gdb_opt_via, + "Drop route points, if they don't have an aquivalent waypoint (hidden points)", NULL, ARGTYPE_BOOL}, + {0, 0, 0, 0, 0} +}; + +/********************************************************************************************************/ /* %%% 1-1 functions from mapsource, should by shared!!! %%% */ +/* + * Add a waypoint that we've already written out to our list + * + */ +void +gdb_add_to_hidden(const waypoint *wpt) +{ + waypoint *tmp = waypt_dupe(wpt); + route_add_wpt(gdb_hidden, tmp); +} + waypoint * gdb_find_wpt_q_by_name(const queue *whichQueue, const char *name) { @@ -72,8 +128,15 @@ gdb_find_wpt_q_by_name(const queue *whichQueue, const char *name) const char * gdb_find_desc_from_icon_number(const int icon, garmin_formats_e garmin_format) { + static char custom[] = "Custom 63"; icon_mapping_t *i; + if (icon >= 500 && icon <= 563) + { + snprintf(custom, sizeof(custom), "Custom %d", icon - 500); + return &custom[0]; + } + for (i = garmin_icon_table; i->icon; i++) { switch (garmin_format) { case MAPSOURCE: @@ -92,6 +155,51 @@ gdb_find_desc_from_icon_number(const int icon, garmin_formats_e garmin_format) return DEFAULTICONDESCR; } +int +gdb_find_icon_number_from_desc(const char *desc, garmin_formats_e garmin_format) +{ + icon_mapping_t *i; + int def_icon = DEFAULTICONVALUE; + int n; + + if (!desc) + return def_icon; + + /* + * If we were given a numeric icon number as a description + * (i.e. 8255), just return that. + */ + n = atoi(desc); + if (n) { + return n; + } + + for (i = garmin_icon_table; i->icon; i++) { + if (case_ignore_strcmp(desc,i->icon) == 0) { + switch (garmin_format) { + case MAPSOURCE: + return i->mpssymnum; + case PCX: + case GARMIN_SERIAL: + return i->pcxsymnum; + default: + fatal(MYNAME ": unknown garmin format"); + } + } + } + return def_icon; +} + +int +gdb_detect_rtept_class(const waypoint *wpt) +{ + if (gdb_find_wpt_q_by_name((queue *)&gdb_hidden->waypoint_list, wpt->shortname) == NULL) + return (int)GDB_HIDDENROUTEWPTCLASS; + else + return (int)GDB_DEFAULTWPTCLASS; +} + + #ifndef UTF8_SUPPORT char *gdb_garmin_to_utf8(const char *s) { @@ -172,31 +280,32 @@ gdb_create_rte_wpt(const char *name, double lat, double lon, double alt) waypoint *wpt; wpt = find_waypt_by_name(name); - if (wpt != NULL) wpt = waypt_dupe(wpt); + if (wpt == NULL) + { + if (gdb_via != 0) return NULL; + wpt = gdb_find_wpt_q_by_name((queue *)&gdb_hidden->waypoint_list, name); + } + if (wpt != NULL) + wpt = waypt_dupe(wpt); else { - gdb_find_wpt_q_by_name((queue *)&gdb_hidden->waypoint_list, name); - if (wpt != NULL) wpt = waypt_dupe(wpt); - else - { - wpt = waypt_new(); - wpt->shortname = xstrdup(name); - wpt->latitude = lat; - wpt->longitude = lon; - wpt->altitude = alt; - wpt->depth = unknown_alt; - } + wpt = waypt_new(); + wpt->shortname = xstrdup(name); + wpt->latitude = lat; + wpt->longitude = lon; + wpt->altitude = alt; + wpt->depth = unknown_alt; } return wpt; } -static size_t -gdb_fread(void *target, size_t size, size_t count, FILE *fin) +size_t +gdb_fread(void *target, size_t size) { size_t result; - result = fread(target, size, count, fin); - if (result < count) + result = fread(target, 1, size, fin); + if (result < size) { if (feof(fin) != 0) fatal(MYNAME ": unexpected end of file \"%s\"!\n", fin_name); @@ -206,8 +315,8 @@ gdb_fread(void *target, size_t size, size_t count, FILE *fin) return result; } -static int -gdb_fread_str(FILE *fin, char *dest, size_t maxlen) +int +gdb_fread_str(char *dest, size_t maxlen) { int c; int res = 0; @@ -232,8 +341,8 @@ gdb_fread_str(FILE *fin, char *dest, size_t maxlen) fatal(MYNAME ": local buffer overflow detected, please report!\n"); } -static int -gdb_fread_le(FILE *fin, void *dest, size_t size, int bit_count, const char *field) +int +gdb_fread_le(void *dest, size_t size, int bit_count, const char *prefix, const char *field) { char buff[32]; unsigned char *c = dest; @@ -242,47 +351,90 @@ gdb_fread_le(FILE *fin, void *dest, size_t size, int bit_count, const char *fiel double *db = dest; if ((bit_count >> 3) != size) - fatal(MYNAME ": internal error (gdb_le_read/%d/%d/%s)!\n", size, bit_count >> 3, field); + fatal(MYNAME "%s: Internal error (gdb_le_read/%d/%d/%s)!\n", prefix, (int)size, bit_count >> 3, field); switch(bit_count) { case 8: - gdb_fread(c, sizeof(*c), 1, fin); - if (gdb_debug) printf(MYNAME ": gdb_fread_le : %d -> %s (0x%x))\n", *c, field, *c); + gdb_fread(c, sizeof(*c)); + if (gdb_debug) + printf(MYNAME "%s: gdb_fread_le : %d -> %s (0x%x))\n", prefix, *c, field, *c); return *c; case 16: if (sizeof(*sh) != size) fatal(MYNAME ": internal decl.!\n"); - gdb_fread(sh, sizeof(*sh), 1, fin); + gdb_fread(sh, sizeof(*sh)); *sh = le_read16(sh); - if (gdb_debug) printf(MYNAME ": gdb_fread_le : %d -> %s (0x%x))\n", *sh, field, *sh); + if (gdb_debug) + printf(MYNAME "%s: gdb_fread_le : %d -> %s (0x%x))\n", prefix, *sh, field, *sh); return *sh; case 32: - gdb_fread(li, 4, 1, fin); + gdb_fread(li, 4); *li = le_read32(li); - if (gdb_debug) printf(MYNAME ": gdb_fread_le : %d -> %s (0x%x)\n", *li, field, *li); + if (gdb_debug) + printf(MYNAME "%s: gdb_fread_le : %d -> %s (0x%x)\n", prefix, *li, field, *li); return *li; case 64: - gdb_fread(buff, sizeof(*db), 1, fin); + gdb_fread(buff, sizeof(*db)); le_read64(db, buff); - if (gdb_debug) printf(MYNAME ": gdb_fread_le : %g -> %s\n", *db, field); + if (gdb_debug) + printf(MYNAME "%s: gdb_fread_le : %g -> %s\n", prefix, *db, field); return 0; default: - fatal(MYNAME ": unsupported bit count (%d) in gdb_le_read!\n", bit_count); + fatal(MYNAME "%s: unsupported bit count (%d) in gdb_le_read!\n", prefix, bit_count); } } -static void -gdb_is_valid(int is, const char *comment) +int +gdb_fread_flag(const char value) /* read one byte and compare to value */ { - if (is == 0) fatal(MYNAME ": error in database structure (%s)!\n", comment); + char c; + + gdb_fread(&c, 1); + return (c == value); } -static void +void +gdb_is_valid(int is, const char *prefix, const char *comment) +{ + if (is == 0) + { + printf(MYNAME ": Reading database \"%s\"\n", fin_name); + fatal(MYNAME "-%s: Found error in data (%s)!\n", prefix, comment); + } +} + +void +gdb_is_validf(int is, const char *prefix, const char *format, ...) +{ + va_list args; + char buff[256]; + + if (is != 0) return; + + va_start(args, format); + if (fin_name != NULL) + printf(MYNAME "-%s: Reading from database \"%s\"\n", prefix, fin_name); + else + printf(MYNAME "-%s: Writing to database \"%s\"\n", prefix, fout_name); + printf(MYNAME "-%s: "); + vprintf(format, args); + puts(""); + + va_end(args); + fatal(""); +} + +/********************************************************************************************************/ +/* %%% read file header */ +/********************************************************************************************************/ + +void gdb_read_file_header(void) { char buff[128]; int i, reclen; + const char *prefix = "read_head"; /* We starts with standard binary read. A gdb_fread_str works too, but if we get a wrong file as input, @@ -293,17 +445,18 @@ gdb_read_file_header(void) */ if (6 != fread(buff, 1, 6, fin)) - fatal(MYNAME ": invalid file \"%s\"!\n", fin_name); + fatal(MYNAME ": Invalid file \"%s\"!\n", fin_name); if (strcmp(buff, "MsRcf") != 0) - fatal(MYNAME ": invalid file \"%s\"!\n", fin_name); + fatal(MYNAME ": Invalid file \"%s\"!\n", fin_name); - gdb_fread(&reclen, 4, 1, fin); + gdb_fread(&reclen, 4); reclen = le_read32(&reclen); - gdb_is_valid(reclen == gdb_fread_str(fin, buff, sizeof(buff)), "header"); - - gdb_is_valid(buff[0] == 'D', "header"); + gdb_is_valid(reclen == gdb_fread_str(buff, sizeof(buff)), prefix, "Invalid record length"); + if (buff[0] != 'D') + fatal(MYNAME ": Invalid file \"%s\"!\n", fin_name); + switch(buff[1]) { case 'k': @@ -313,40 +466,44 @@ gdb_read_file_header(void) gdb_ver = 2; break; default: - fatal(MYNAME ": non supported gdb version!\n"); + fatal(MYNAME ": Non supported GDB version!\n"); } if (global_opts.verbose_status > 0) - printf(MYNAME ": found Garmin GPS Database version %d\n", gdb_ver); + printf(MYNAME ": Found Garmin GPS Database version %d.0\n", gdb_ver); - gdb_fread(&reclen, 4, 1, fin); + gdb_fread(&reclen, 4); reclen = le_read32(&reclen); - gdb_is_valid(reclen < sizeof(buff), "header"); - gdb_fread(buff, reclen, 1, fin); + gdb_is_valid(reclen < sizeof(buff), prefix, "Invalid record length"); + gdb_fread(buff, reclen); - gdb_is_valid(0 == gdb_fread_str(fin, buff, sizeof(buff)), "header"); + gdb_is_valid(0 == gdb_fread_str(buff, sizeof(buff)), prefix, "header"); - i = gdb_fread_str(fin, buff, sizeof(buff)); - gdb_is_valid((i == 9) && (strcmp(buff, "MapSource") == 0), "header"); + i = gdb_fread_str(buff, sizeof(buff)); + gdb_is_valid((i == 9) && (strcmp(buff, "MapSource") == 0), prefix, "MapSource magic"); } +/********************************************************************************************************/ +/* %%% read waypoint */ +/********************************************************************************************************/ + waypoint * gdb_read_wpt(const size_t fileofs, int *wptclass) { - char xname[MPSNAMEBUFFERLEN]; - char xdesc[MPSDESCBUFFERLEN]; - char xnotes[MPSNOTESBUFFERLEN]; + char xname[GDB_NAME_BUFFERLEN]; + char xdesc[GDB_DESCR_BUFFERLEN]; + char xnotes[GDB_NOTES_BUFFERLEN]; int xclass; int xlat, xlon, xdisplay, xcolour, xicon, xtime; short xcat; double xdepth = unknown_alt; double xalt = unknown_alt; + double xproximity = unknown_alt; waypoint *res; - - char *ctmp; char buff[128]; - size_t pos, delta; + + const char *prefix = "wpt_read"; /********************************************************************************************************/ @@ -377,67 +534,53 @@ gdb_read_wpt(const size_t fileofs, int *wptclass) */ /********************************************************************************************************/ - gdb_is_valid(gdb_fread_str(fin, xname, sizeof(xname)) > 0, "new waypoint"); + gdb_is_valid(gdb_fread_str(xname, sizeof(xname)) > 0, prefix, "new waypoint"); gdb_convert_name_buff(xname, sizeof(xname)); - gdb_fread_le(fin, &xclass, sizeof(xclass), 32, "xclass"); - gdb_fread_str(fin, buff, sizeof(buff)); /* country */ + gdb_fread_le(&xclass, sizeof(xclass), 32, prefix, "class"); + gdb_fread_str(buff, sizeof(buff)); /* country */ - gdb_fread(buff, 22, 1, fin); - xlat = gdb_fread_le(fin, &xlat, sizeof(xlat), 32, "xlat"); /* latitude */ - xlon = gdb_fread_le(fin, &xlon, sizeof(xlon), 32, "xlon"); /* latitude */ + gdb_fread(buff, 22); + xlat = gdb_fread_le(&xlat, sizeof(xlat), 32, prefix, "latitude"); + xlon = gdb_fread_le(&xlon, sizeof(xlon), 32, prefix, "longitude"); - gdb_fread(buff, 1, 1, fin); - if (buff[0] == 1) /* altitude flag */ - { - gdb_fread_le(fin, &xalt, sizeof(xalt), 64, "xalt"); /* altitude */ - } + if (gdb_fread_flag(1)) /* altitude flag */ + gdb_fread_le(&xalt, sizeof(xalt), 64, prefix, "altitude"); - gdb_fread_str(fin, xdesc, sizeof(xdesc)); + gdb_fread_str(xdesc, sizeof(xdesc)); /* description */ gdb_convert_name_buff(xdesc, sizeof(xdesc)); - gdb_fread(buff, 1, 1, fin); /* proximity flag */ - if (buff[0] == 1) - { - gdb_fread(buff, 8, 1, fin); /* proximity */ - } + if (gdb_fread_flag(1)) /* proximity flag */ + gdb_fread_le(&xproximity, sizeof(xproximity), 64, prefix, "proximity"); - xdisplay = gdb_fread_le(fin, &xdisplay, sizeof(xdisplay), 32, "xdisplay"); - xcolour = gdb_fread_le(fin, &xcolour, sizeof(xcolour), 32, "xcolour"); - xicon = gdb_fread_le(fin, &xicon, sizeof(xicon), 32, "xicon"); + xdisplay = gdb_fread_le(&xdisplay, sizeof(xdisplay), 32, prefix, "display"); + xcolour = gdb_fread_le(&xcolour, sizeof(xcolour), 32, prefix, "colour"); + xicon = gdb_fread_le(&xicon, sizeof(xicon), 32, prefix, "icon"); - /* ToDo: convert the icon !!! */ - - gdb_fread_str(fin, buff, sizeof(buff)); /* city */ - gdb_fread_str(fin, buff, sizeof(buff)); /* state */ - gdb_fread_str(fin, buff, sizeof(buff)); /* facility */ - gdb_fread(buff, 1, 1, fin); /* unknown */ + gdb_fread_str(buff, sizeof(buff)); /* city */ + gdb_fread_str(buff, sizeof(buff)); /* state */ + gdb_fread_str(buff, sizeof(buff)); /* facility */ - gdb_fread(buff, 1, 1, fin); /* depth flag */ - if (buff[0] == 1) - { - gdb_fread_le(fin, &xdepth, sizeof(xdepth), 64, "xdepth"); /* depth */ - } + gdb_fread(buff, 1); /* unknown */ - gdb_fread(buff, 1, 1, fin); - gdb_fread(buff, 1, 1, fin); - gdb_fread(buff, 1, 1, fin); + if (gdb_fread_flag(1)) /* depth flag */ + gdb_fread_le(&xdepth, sizeof(xdepth), 64, prefix, "depth"); - if (buff[0] != 0) - gdb_fread(buff, 3, 1, fin); + gdb_fread(buff, 1); + gdb_fread(buff, 1); + + if (gdb_fread_flag(0)) + gdb_fread(buff, 4); else - gdb_fread(buff, 4, 1, fin); + gdb_fread(buff, 3); - gdb_fread_str(fin, xnotes, sizeof(xnotes)); + gdb_fread_str(xnotes, sizeof(xnotes)); gdb_convert_name_buff(xnotes, sizeof(xnotes)); - xcat = gdb_fread_le(fin, &xcat, sizeof(xcat), 16, "xcat"); + xcat = gdb_fread_le(&xcat, sizeof(xcat), 16, prefix, "category"); - gdb_fread(buff, 1, 1, fin); /* temperature flag */ - if (buff[0] == 1) - { - gdb_fread(buff, 8, 1, fin); /* temperature */ - } + if (gdb_fread_flag(1)) /* temperature flag */ + gdb_fread(buff, 8); /* temperature */ /* Here comes 1 .. 6 unknown bytes !!! 6 only if class > 0 !!! @@ -445,23 +588,22 @@ gdb_read_wpt(const size_t fileofs, int *wptclass) pos = ftell(fin); delta = fileofs - pos; - gdb_is_valid(delta > 0, "waypoint final"); + gdb_is_valid(delta > 0, prefix, "waypoint final"); if ((delta & 1) == 0) { - gdb_fread(buff, 1, 1, fin); + gdb_fread(buff, 1); delta--; } xtime = 0; - gdb_fread(buff, 1, 1, fin); - if (buff[0] == 1) + if (gdb_fread_flag(1)) { - gdb_is_valid(delta==5, "??? waypoint time ???"); - gdb_fread_le(fin, &xtime, sizeof(xtime), 32, "xtime"); + gdb_is_valid(delta==5, prefix, "Waypoint time"); + gdb_fread_le(&xtime, sizeof(xtime), 32, prefix, "time"); } else - gdb_is_valid(delta==1, "no waypoint time"); + gdb_is_valid(delta==1, prefix, "No waypoint time"); *wptclass = xclass; @@ -472,204 +614,182 @@ gdb_read_wpt(const size_t fileofs, int *wptclass) res->latitude = GPS_Math_Semi_To_Deg(xlat); res->longitude = GPS_Math_Semi_To_Deg(xlon); res->altitude = xalt; + res->depth = xdepth; + res->proximity = xproximity; res->creation_time = xtime; +#if 0 + res->garmin_data = xcalloc(1, sizeof(garmin_data_t)); + res->garmin_data->colour = xcolour; + res->garmin_data->category = xcat; + res->garmin_data->display = xdisplay; +#endif /* might need to change this to handle version dependent icon handling */ res->icon_descr = gdb_find_desc_from_icon_number(xicon, MAPSOURCE); - - gdb_is_valid(fabs(res->latitude) <= 90.0 && fabs(res->longitude) <= 180.0, " - wpt read: invalid lat or lon"); + + gdb_is_validf(fabs(res->latitude) <= 90.0, prefix, "%s has invalid latitude (%f)", + res->shortname, res->latitude); return res; } +/********************************************************************************************************/ +/* %%% read route */ +/********************************************************************************************************/ + route_head * gdb_read_route(void) { - char xname[MPSNAMEBUFFERLEN]; - char xwptname[MPSNAMEBUFFERLEN]; + char xname[GDB_NAME_BUFFERLEN]; + char xwptname[GDB_NAME_BUFFERLEN]; int xclass; double xalt; - double xlat, xlon; + double xlat = 0; /* compiler warnings */ + double xlon = 0; /* compiler warnings */ char buff[256]; - int count; - int checked; + int count, origin; int isteps, ilink; int semilat, semilon; + int maxlat, maxlon, minlon, minlat; + char auto_name; route_head *route; waypoint *wpt; - int i, j; - size_t curpos; + const char *prefix = "rte_read_head"; + const char *prefix1 = "rte_read_loop"; + const char *prefix2 = "rte_ils_loop"; + const char *prefix3 = "rte_read_final"; - gdb_is_valid(gdb_fread_str(fin, xname, sizeof(xname)) > 0, "route start"); + gdb_is_valid(gdb_fread_str(xname, sizeof(xname)) > 0, prefix, "Route has no name"); gdb_convert_name_buff(xname, sizeof(xname)); - - gdb_fread_le(fin, buff, 2, 16, "auto_name"); - gdb_fread_le(fin, buff, 4, 32, "max_lat"); - gdb_fread_le(fin, buff, 4, 32, "max_lon"); - gdb_fread(buff, 1, 1, fin); - if (buff[0] == 1) gdb_fread_le(fin, buff, 8, 64, "max_alt"); + gdb_fread_le(&auto_name, sizeof(auto_name), 8, prefix, "auto name"); + if (gdb_fread_flag(0)) /* max. data flag */ + { + gdb_fread_le(buff, 4, 32, prefix, "max. latitude"); + gdb_fread_le(buff, 4, 32, prefix, "max. longitude"); + + gdb_fread(buff, 1); + if (buff[0] == 1) gdb_fread_le(buff, 8, 64, prefix, "max. altitude"); - gdb_fread_le(fin, buff, 4, 32, "min_lat"); - gdb_fread_le(fin, buff, 4, 32, "min_lon"); + gdb_fread_le(buff, 4, 32, prefix, "min. latitude"); + gdb_fread_le(buff, 4, 32, prefix, "min. longitude"); - gdb_fread(buff, 1, 1, fin); - if (buff[0] == 1) gdb_fread_le(fin, buff, 8, 64, "min_alt"); + gdb_fread(buff, 1); + if (buff[0] == 1) + gdb_fread_le(buff, 8, 64, prefix, "min. altitude"); + } - gdb_fread_le(fin, &count, sizeof(count), 32, "rte_count"); - if (count <= 0) return NULL; + gdb_fread_le(&count, sizeof(count), 32, prefix, "count"); + + if (count == 0) + fatal(MYNAME "%s: !!! Empty routes are not allowed !!!\n", prefix); route = route_head_alloc(); route->rte_name = xstrdup(xname); route_add_head(route); -#if GDB_DEBUG - printf(MYNAME " - route: \"%s\" with %d point(s)\n", route->rte_name, count); +#ifdef GDB_DEBUG + printf(MYNAME " - route: \"%s\" with %d point(s)\n", xname, count); #endif - - if (count <= 0) return route; - - count--; + origin = count; while (count--) { - gdb_fread_str(fin, xwptname, sizeof(xwptname)); /* name */ + gdb_fread_str(xwptname, sizeof(xwptname)); /* waypoint name */ gdb_convert_name_buff(xwptname, sizeof(xwptname)); + + gdb_fread_le(&xclass, sizeof(xclass), 32, prefix1, "class"); /* class */ + gdb_fread_str(buff, sizeof(buff)); /* country */ - gdb_fread_le(fin, &xclass, sizeof(xclass), 32, "xclass"); /* class */ - gdb_fread_str(fin, buff, sizeof(buff)); /* country */ - - gdb_fread(buff, 22, 1, fin); /* sub class data */ - i = 0; - while (i < sizeof(buff)) - { - gdb_fread(&buff[i], 1, 1, fin); - if (buff[i] == 0) break; - i++; - } + gdb_fread(buff, 22); /* sub class data */ + gdb_fread(buff, 1); + gdb_is_valid(buff[0] == 0, prefix1, "Should by zero byte (1)"); /* The next thing is the unknown 0x03 0x00 .. 0x00 (18 bytes) */ - gdb_fread(buff, 18, 1, fin); - - gdb_fread_le(fin, &isteps, sizeof(isteps), 32, "isteps"); + /* OK: this should be, but i've seen exceptions (...cannot verify the first byte */ + gdb_fread(buff, 18); + + gdb_fread_le(&isteps, sizeof(isteps), 32, prefix1, "interlink steps"); - if (isteps <= 0) return route; + if (isteps <= 0) /* ??? end of route or error ??? */ + { + gdb_is_valid(count == 0, prefix3, "Zero interlink steps within route"); + + gdb_fread(buff, 1); + gdb_is_valid((buff[0] == 1), prefix3, "last seq.(1)"); + + if (gdb_ver > 1) + gdb_fread(buff, 8); /* Unknown 8 bytes since gdb v2 */ + + gdb_fread(buff, 1); + gdb_is_valid((buff[0] == 0), prefix3, "last seq.(2)"); + + wpt = gdb_create_rte_wpt(xwptname, xlat, xlon, xalt); + if (wpt != NULL) + route_add_wpt(route, wpt); + return route; + } - gdb_fread_le(fin, &semilat, sizeof(semilat), 32, "semilat"); - gdb_fread_le(fin, &semilon, sizeof(semilon), 32, "semilon"); + gdb_fread_le(&semilat, sizeof(semilat), 32, prefix1, "semi-latitude"); + gdb_fread_le(&semilon, sizeof(semilon), 32, prefix1, "semi-longitude"); xlat = GPS_Math_Semi_To_Deg(semilat); xlon = GPS_Math_Semi_To_Deg(semilon); - gdb_is_valid(fabs(xlat) <= 90.0 && fabs(xlon) <= 180.0, " - rte: read loop: invalid lat or lon"); + gdb_is_validf(fabs(xlat) <= 90.0, prefix1, "Invalid latitude (%f)", xlat); - xalt = unknown_alt; - gdb_fread(buff, 1, 1, fin); /* altitude flag */ - if (buff[0] == 1) - { - gdb_fread_le(fin, &xalt, sizeof(xalt), 64, "xalt"); - } + if (gdb_fread_flag(1)) /* altitude flag */ + gdb_fread_le(&xalt, sizeof(xalt), 64, prefix1, "altitude"); + else + xalt = unknown_alt; wpt = gdb_create_rte_wpt(xwptname, xlat, xlon, xalt); - route_add_wpt(route, wpt); + if (wpt != NULL) + route_add_wpt(route, wpt); - for (ilink = isteps-1; ilink > 0; ilink--) + while (--isteps > 0) { - gdb_fread_le(fin, &semilat, sizeof(semilat), 32, "semilat"); - gdb_fread_le(fin, &semilon, sizeof(semilon), 32, "semilon"); - gdb_fread(buff, 1, 1, fin); /* altitude flag */ - if (buff[0] == 1) gdb_fread_le(fin, &xalt, sizeof(xalt), 64, "xalt"); + gdb_fread_le(&semilat, sizeof(semilat), 32, prefix2, "semi-latitude"); + gdb_fread_le(&semilon, sizeof(semilon), 32, prefix2, "semi-longitude"); + gdb_fread(buff, 1); /* altitude flag */ + if (buff[0] == 1) + gdb_fread_le(&xalt, sizeof(xalt), 64, prefix2, "altitude"); + xlat = GPS_Math_Semi_To_Deg(semilat); xlon = GPS_Math_Semi_To_Deg(semilon); - gdb_is_valid(fabs(xlat) <= 90.0 && fabs(xlon) <= 180.0, " - rte/ils: read loop: invalid lat or lon"); + gdb_is_validf(fabs(xlat) <= 90.0, prefix2, "Invalid latitude (%f)", xlat); } - gdb_fread(buff, 1, 1, fin); /* NULL */ - gdb_is_valid(buff[0] == 0, "should be zero byte"); - - gdb_fread(buff, 4, 1, fin); /* link max lat */ - gdb_fread(buff, 4, 1, fin); /* link max lon */ - gdb_fread(buff, 1, 1, fin); - if (buff[0] == 1) - { - gdb_fread(buff, 8, 1, fin); /* link max alt validity + alt */ - } - gdb_fread(buff, 4, 1, fin); /* link min lat */ - gdb_fread(buff, 4, 1, fin); /* link min lon */ + gdb_fread(buff, 1); + gdb_is_valid(buff[0] == 0, prefix1, "\"Zero\" byte expected"); - gdb_fread(buff, 1, 1, fin); - if (buff[0] == 1) - { - gdb_fread(buff, 8, 1, fin); /* link min alt validity + alt */ - } + gdb_fread_le(&maxlat, sizeof(maxlat), 32, prefix1, "max. latitude"); + gdb_fread_le(&maxlon, sizeof(maxlon), 32, prefix1, "max. longitude"); - /* find the end of the record */ + if (gdb_fread_flag(1)) /* link max alt validity + alt */ + gdb_fread(buff, 8); - curpos = ftell(fin); - - /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - the stuff here is very tricky and did not base on any - any knowledgement, but seems to work. - The final structure varied from file to file and i - could not find any connection with data, gdb version - and any unknown bytes and bits. Hmm. - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + gdb_fread_le(&minlat, sizeof(minlat), 32, prefix1, "min. latitude"); + gdb_fread_le(&minlon, sizeof(minlon), 32, prefix1, "min. longitude"); - memset(buff, 0, sizeof(buff)); - checked = 0; - - j = 18; - while (checked == 0 && j-- > 0) - { - for (i=1; i<8; i++) buff[i-1] = buff[i]; - gdb_fread(&buff[7], 1, 1, fin); - for (i=0; i<8; i++) - { - if (buff[i] != -1) break; - if (i == 7) checked = 1; - } + if (gdb_fread_flag(1)) /* link min alt validity + alt */ + gdb_fread(buff, 2 * sizeof(int)); - } - if (checked == 0) - { - fseek(fin, curpos, SEEK_SET); - } - else - { - while (1) - { - gdb_fread(buff, 1, 1, fin); - if (buff[0] != -1) - { - fseek(fin, ftell(fin)-1, SEEK_SET); - break; - } - } - - } + if (gdb_ver > 1) + gdb_fread(buff, 8); /* unknown 8 bytes since gdb v2 */ } - gdb_fread_str(fin, xwptname, sizeof(xwptname)); /* name */ - gdb_convert_name_buff(xwptname, sizeof(xwptname)); - -#if GDB_DEBUG - printf(MYNAME " - rte/fin: \"%s\"\n", xwptname); -#endif - gdb_fread_le(fin, &xclass, sizeof(xclass), 32, "xclass"); /* class */ - gdb_fread_str(fin, buff, sizeof(buff)); /* country */ - - wpt = gdb_create_rte_wpt(xwptname, xlat, xlon, xalt); - route_add_wpt(route, wpt); + /* This should normally never happen; end of route is handled in main loop before this */ - return route; + fatal(MYNAME "-%s: Unexpected end of route \"%s\"!", prefix1, xname); } route_head * gdb_read_track(const size_t max_file_pos) { - char xname[MPSNAMEBUFFERLEN]; + char xname[GDB_NAME_BUFFERLEN]; unsigned char xdisplay; int xcolour; int xlat; @@ -685,39 +805,40 @@ gdb_read_track(const size_t max_file_pos) route_head *track; waypoint *wpt; - gdb_fread_str(fin, xname, sizeof(xname)); + const char *prefix0 = "trk_read"; + const char *prefix = "trk_read_loop"; + + gdb_fread_str(xname, sizeof(xname)); gdb_convert_name_buff(xname, sizeof(xname)); - gdb_fread_le(fin, &xdisplay, sizeof(xdisplay), 8, "xdisplay"); - gdb_fread_le(fin, &xcolour, sizeof(xcolour), 32, "xcolour"); - gdb_fread_le(fin, &count, sizeof(count), 32, "count"); + gdb_fread_le(&xdisplay, sizeof(xdisplay), 8, prefix0, "display"); + gdb_fread_le(&xcolour, sizeof(xcolour), 32, prefix0, "colour"); + gdb_fread_le(&count, sizeof(count), 32, prefix0, "count"); track = route_head_alloc(); track->rte_name = xstrdup(xname); track_add_head(track); - while (count > 0) + while (count--) { - count--; - - gdb_fread_le(fin, &xlat, sizeof(xlat), 32, "xlat"); - gdb_fread_le(fin, &xlon, sizeof(xlon), 32, "xlon"); + gdb_fread_le(&xlat, sizeof(xlat), 32, prefix, "latitude"); + gdb_fread_le(&xlon, sizeof(xlon), 32, prefix, "longitude"); - gdb_fread(buff, 1, 1, fin); /* altitude flag */ + gdb_fread(buff, 1); /* altitude flag */ if (buff[0] == 1) - gdb_fread_le(fin, &xalt, sizeof(xalt), 64, "xalt"); + gdb_fread_le(&xalt, sizeof(xalt), 64, prefix, "altitude"); - gdb_fread(buff, 1, 1, fin); /* date/time flag */ + gdb_fread(buff, 1); /* date/time flag */ if (buff[0] == 1) - gdb_fread_le(fin, &xtime, sizeof(xtime), 32, "xtime"); + gdb_fread_le(&xtime, sizeof(xtime), 32, prefix, "time"); - gdb_fread(buff, 1, 1, fin); /* depth flag */ + gdb_fread(buff, 1); /* depth flag */ if (buff[0] == 1) - gdb_fread_le(fin, &xdepth, sizeof(xdepth), 64, "xdepth"); + gdb_fread_le(&xdepth, sizeof(xdepth), 64, prefix, "depth"); - gdb_fread(buff, 1, 1, fin); + gdb_fread(buff, 1); /* temperature flag */ if (buff[0] == 1) - gdb_fread_le(fin, &xtemp, sizeof(xtemp), 64, "xtemp"); + gdb_fread_le(&xtemp, sizeof(xtemp), 64, prefix, "temperature"); wpt = waypt_new(); @@ -728,98 +849,828 @@ gdb_read_track(const size_t max_file_pos) wpt->altitude = xalt; wpt->depth = xdepth; - gdb_is_valid(fabs(wpt->latitude) <= 90.0 && fabs(wpt->longitude) <= 180.0, " - trk read loop: invalid lat or lon"); + gdb_is_validf(fabs(wpt->latitude) <= 90.0, prefix, "Invalid latitude (%f)", wpt->latitude); route_add_wpt(track, wpt); } + gdb_fread(buff, 1); + return track; } +/*******************************************************************************/ -static void +void gdb_read_data(void) { - int reclen, done; + int reclen, warnings; char typ; - size_t curpos, deltapos; - waypoint *wpt; + size_t curpos, anchor; int wptclass; - done = 0; - while (!feof(fin) && (done == 0)) - { + const char *prefix = "main_read_loop"; + + gdb_hidden = route_head_alloc(); + track_add_head(gdb_hidden); + + warnings = 0; + + anchor = ftell(fin); - gdb_fread_le(fin, &reclen, sizeof(reclen), 32, "reclen"); - gdb_is_valid(reclen > 0 && reclen < 0x1F00000, "gdb data loop"); - gdb_fread(&typ, 1, 1, fin); + /* we go twice through the file to keep sure, all waypoints + are loaded before any route has to be handled */ + + while (feof(fin) == 0) + { + + gdb_fread_le(&reclen, sizeof(reclen), 32, prefix, "record length"); + gdb_is_valid(reclen > 0 && reclen < 0x1F00000, prefix, "record length"); + gdb_fread(&typ, 1); curpos = ftell(fin); - switch(typ) + if (typ == 'W') { - case 'W': - wpt = gdb_read_wpt(curpos + reclen, &wptclass); - if (wpt != NULL ) + int delta; + waypoint *wpt; + + wpt = gdb_read_wpt(curpos + reclen, &wptclass); + if (wpt != NULL ) + { + if (wptclass == 0) + waypt_add(wpt); + else if (gdb_via == 0) + route_add_wpt(gdb_hidden, wpt); + else + waypt_free(wpt); + } + delta = (int)((curpos + reclen) - ftell(fin)); + if (delta != 0) + { + if ((warnings & 1) == 0) { - if (wptclass == 0) - waypt_add(wpt); - else - route_add_wpt(gdb_hidden, wpt); + warnings |= 1; + warning(MYNAME "-%s: At least one incomplete waypoint read (%d byte(s) left).\n", prefix, delta); } - deltapos = (curpos+reclen)-ftell(fin); - break; - case 'T': - gdb_read_track(curpos + reclen); - break; - case 'R': - gdb_read_route(); - break; - case 'L': - break; - case 'V': - done = 1; - break; - default: - printf(MYNAME ": found unknown record type \"%c\"!\n", typ); + fseek(fin, curpos + reclen, SEEK_SET); + } + continue; } + else if (typ == 'V') + break; + fseek(fin, curpos + reclen, SEEK_SET); } + + clearerr(fin); + fseek(fin, anchor, SEEK_SET); + + while (feof(fin) == 0) + { + gdb_fread_le(&reclen, sizeof(reclen), 32, prefix, "record length"); + gdb_is_valid(reclen > 0 && reclen < 0x1F00000, prefix, "record length"); + gdb_fread(&typ, 1); + + curpos = ftell(fin); + + if ((typ == 'R') || (typ == 'T')) + { + int flag, delta; + + if (typ == 'R') + { + gdb_read_route(); + flag = 2; + } + else + { + gdb_read_track(curpos + reclen); + flag = 4; + } + delta = (int)((curpos + reclen) - ftell(fin)); + if (delta != 0) + { + if ((delta != reclen) && ((warnings & flag) == 0)) + { + warnings |= flag; + warning(MYNAME "-%s: At least one incomplete %s (gdb v%d.0, %d byte(s) left).\n", + prefix, (typ == 'R') ? "route" : "track", gdb_ver, delta); + } + fseek(fin, curpos + reclen, SEEK_SET); + } + } + else + { + if (typ == 'V') break; + + switch(typ) + { + case 'D': break; + case 'L': break; + case 'W': break; + default: warning(MYNAME "-%s: Found unknown record type \"%c\"!\n", prefix, typ); + } + fseek(fin, curpos + reclen, SEEK_SET); + } + } + + /* finally kill our temporary queue */ + track_del_head(gdb_hidden); +} + +/*******************************************************************************/ +/* %%% write support %%% */ +/*******************************************************************************/ + +/* helpers */ + +waypoint ** +gdb_route_point_list(const route_head *route, int *count) +{ + waypoint **result; + queue *elem, *tmp; + int i = 0; + + QUEUE_FOR_EACH((queue *)&route->waypoint_list, elem, tmp) + { + waypoint *wpt = (waypoint *)elem; + if ((gdb_via == 0) || + (gdb_detect_rtept_class(wpt) == GDB_DEFAULTWPTCLASS)) i++; + } + + *count = i; + if (i == 0) return NULL; + + result = xcalloc(i, sizeof(*result)); + + i = 0; + QUEUE_FOR_EACH((queue *)&route->waypoint_list, elem, tmp) + { + waypoint *wpt = (waypoint *)elem; + if ((gdb_via == 0) || + (gdb_detect_rtept_class(wpt) == GDB_DEFAULTWPTCLASS)) + result[i++] = wpt; + } + + return result; +} + +void +gdb_fwrite(const void *data, const size_t size) +{ + fwrite(data, size, 1, fout); +} + +void +gdb_fwrite_str(const char *str, const int len) +{ + + if (len >= 0) + gdb_fwrite(str, len); /* write a string with fixed length */ + else + { + char *tmp = str_utf8_to_cp1252((str != NULL) ? str : ""); + gdb_fwrite(tmp, strlen(tmp) + 1); + xfree(tmp); + } +} + +void +gdb_fwrite_le(const void *data, const size_t size) +{ + int i; + short s; + char buff[8]; + + switch(size) + { + case 1: + gdb_fwrite(data, 1); + break; + + case 2: /* sizeof(short): */ + s = *(short *)data; + le_write16(&s, s); + gdb_fwrite(&s, 2); + break; + + case 4: /* sizeof(int): */ + i = *(int *)data; + le_write32(&i, i); + gdb_fwrite(&i, 4); + break; + + case 8: /* sizeof(double): */ + le_read64(buff, data); + gdb_fwrite(buff, 8); + break; + + default: + fatal(MYNAME "-write_le: Unsupported data size (%ld)!\n", size); + } +} + +void +gdb_fwrite_alt(const double alt, const double unknown_value) +{ + char c0 = 0; + char c1 = 1; + + if (alt != unknown_value) /* proximity / depth / altitude */ + { + gdb_fwrite(&c1, 1); + gdb_fwrite_le(&alt, sizeof(alt)); + } + else + gdb_fwrite(&c0, 1); /* no value */ +} + +void +gdb_fwrite_int(const int data) +{ + gdb_fwrite_le(&data, sizeof(data)); +} + +void +gdb_fwrite_icon(const waypoint *wpt) /* partly taken from mapsource.c */ +{ + int icon; + char buff[128]; + + if ( /* handle custom icons, which are linked to -2 in garmin_tables.c */ + (wpt->icon_descr != NULL) && + (sscanf(wpt->icon_descr, "%s%d", buff, &icon) == 2) && + (case_ignore_strcmp(buff, "Custom") == 0) && + (icon >= 0) && (icon <= 63) + ) + { + icon += 500; + } + else + { + /* might need to change this to handle version dependent icon handling */ + icon = gdb_find_icon_number_from_desc(wpt->icon_descr, MAPSOURCE); + if (get_cache_icon(wpt) /* && wpt->icon_descr && (strcmp(wpt->icon_descr, "Geocache Found") != 0)*/) + { + icon = gdb_find_icon_number_from_desc(get_cache_icon(wpt), MAPSOURCE); + } + } + gdb_fwrite_le(&icon, sizeof(icon)); +} + +/*******************************************************************************/ +/* %%% write file header %%% */ +/*-----------------------------------------------------------------------------*/ + +void +gdb_write_file_header(const struct tm *tm) +{ + char buff[128]; + char *c; + int len; + + gdb_fwrite_str("MsRcf", -1); + gdb_fwrite_int(2); + + strncpy(buff, "Dx", sizeof(buff)); + buff[1] = 'k' - 1 + gdb_ver; + gdb_fwrite_str(buff, -1); + +#if 0 + strncpy(buff, "A].SQA*Dec 27 2004*17:40:51", sizeof(buff)); /* MapSource V6.5 */ +#else + /* This is our "Watermark" to show this file was not created by MapSource */ + /* !!! We should define the date use through Makefile !!! */ + strncpy(buff, "A].GPSBabel*Jul 29 2005*09:52:51", sizeof(buff)); /* gpsbabel V1.2.6 */ +#endif + len = strlen(buff); + buff[2] = 2; + + c = buff; + while ((c = strchr(c, '*'))) *c++ = '\0'; + + gdb_fwrite_int(len); + gdb_fwrite_str(buff, len + 1); + + gdb_fwrite_str("MapSource", -1); /* MapSource magic */ +} + +/*******************************************************************************/ +/* %%% write waypoints %%% */ +/*-----------------------------------------------------------------------------*/ + +void +gdb_write_waypt(const waypoint *wpt, const int hidden) +{ + int i; + char ffbuf[32], zbuf[32]; + char c0 = 0; + char c1 = 1; + + gdb_is_validf((fabs(wpt->latitude) <= 90), "wpt_write", + "%s: Invalid latitude (%f) detected\n", wpt->shortname, wpt->latitude); + + memset(ffbuf, 0xFF, sizeof(ffbuf)); + memset(zbuf, 0x00, sizeof(zbuf)); + + gdb_fwrite_str(wpt->shortname, -1); + + gdb_fwrite_int( (hidden != 0) ? + GDB_HIDDENROUTEWPTCLASS : GDB_DEFAULTWPTCLASS); /* class */ + gdb_fwrite_str("", -1); /* country */ + + gdb_fwrite(zbuf, 4); /* subclass part 1 */ + gdb_fwrite(ffbuf, 12); /* subclass part 2 */ + gdb_fwrite(zbuf, 2); /* subclass part 3 */ + gdb_fwrite(ffbuf, 4); /* unknown */ + + gdb_fwrite_int(GPS_Math_Deg_To_Semi(wpt->latitude)); + gdb_fwrite_int(GPS_Math_Deg_To_Semi(wpt->longitude)); + + gdb_fwrite_alt(wpt->altitude, unknown_alt); /* altitude */ + gdb_fwrite_str(wpt->description, -1); /* description */ + gdb_fwrite_alt(wpt->proximity, unknown_alt); /* proximity */ + +#if 0 + gdb_fwrite_int((wpt->garmin_data != NULL) ? wpt->garmin_data->display : 0); /* display */ + gdb_fwrite_int((wpt->garmin_data != NULL) ? wpt->garmin_data->colour : 0); /* colour */ +#else + gdb_fwrite_int(0); /* display */ + gdb_fwrite_int(0); /* colour */ +#endif + gdb_fwrite_icon(wpt); /* icon */ + gdb_fwrite_str("", -1); /* city */ + gdb_fwrite_str("", -1); /* state */ + gdb_fwrite_str("", -1); /* facility */ + gdb_fwrite(zbuf, 1); /* unknown */ + gdb_fwrite_alt(wpt->depth, unknown_alt); /* depth */ + + gdb_fwrite(zbuf, 3); /* three unknown bytes */ + gdb_fwrite(zbuf, 4); /* four unknown bytes */ + + gdb_fwrite_str(wpt->notes, -1); /* notes */ + +#if 0 + if (gdb_opt_category != NULL) /* category */ + i = gdb_category; + else + i = (wpt->garmin_data != NULL) ? wpt->garmin_data->category : 0; +#else + i = gdb_category; +#endif + gdb_fwrite_le(&i, 2); + + gdb_fwrite(zbuf, 1); /* temperature flag */ + + if (wpt->creation_time != 0) /* creation time */ + { + gdb_fwrite(&c1, 1); + gdb_fwrite_int(wpt->creation_time); + } + else + gdb_fwrite(&c0, 1); + +} + +static void +gdb_write_waypt_cb(const waypoint *wpt) /* called by waypt_disp over all waypoints */ +{ + int reclen; + size_t pos; + + /* check for duplicate waypoints */ + if (NULL != gdb_find_wpt_q_by_name((queue *)&gdb_hidden->waypoint_list, wpt->shortname)) + return; + + gdb_fwrite_int(0); + gdb_fwrite_str("W", 1); + + pos = ftell(fout); + gdb_write_waypt(wpt, 0); + reclen = ftell(fout) - pos; + + fseek(fout, pos - 5, SEEK_SET); + gdb_fwrite_int(reclen); + + fseek(fout, pos + reclen, SEEK_SET); + + route_add_wpt(gdb_hidden, waypt_dupe(wpt)); /* add tis point to our internal queue */ +} + +static void +gdb_write_rtewpt_cb(const waypoint *wpt) /* called by waypt_disp (route points) */ +{ + int reclen; + size_t pos; + waypoint *tmp; + + tmp = gdb_find_wpt_q_by_name((queue *)&gdb_hidden->waypoint_list, wpt->shortname); + if (tmp == NULL) + { + tmp = find_waypt_by_name(wpt->shortname); + + gdb_fwrite_int(0); + gdb_fwrite_str("W", 1); + + pos = ftell(fout); + gdb_write_waypt(wpt, (tmp == NULL)); + reclen = ftell(fout) - pos; + + fseek(fout, pos - 5, SEEK_SET); + gdb_fwrite_int(reclen); + + fseek(fout, pos + reclen, SEEK_SET); + + route_add_wpt(gdb_hidden, waypt_dupe(wpt)); /* add tis point to our internal queue */ + } } -/* %%% gobal callbacks %%% */ +/*******************************************************************************/ +/* %%% write routes %%% */ +/*-----------------------------------------------------------------------------*/ -static void gdb_rd_init(const char *fname) +void +gdb_write_route(const route_head *route, const waypoint **list, const int count) { + int i, wpt_class; + char buff[128], zbuff[32], ffbuff[32]; + waypoint *prev = NULL; + const char c0 = 0; + const char c1 = 1; + const char c3 = 3; + double maxlat = -90; + double minlat = +90; + double maxlon = -180; + double minlon = +180; + double maxalt = -unknown_alt; + double minalt = +unknown_alt; + + memset(zbuff, 0, sizeof(zbuff)); + memset(ffbuff, 0xFF, sizeof(ffbuff)); + + for (i = 0; i < count; i++) + { + const waypoint *wpt = list[i]; + + if (wpt->latitude > maxlat) maxlat = wpt->latitude; + if (wpt->latitude < minlat) minlat = wpt->latitude; + if (wpt->longitude > maxlon) maxlon = wpt->longitude; + if (wpt->longitude < minlon) minlon = wpt->longitude; + if (wpt->altitude != unknown_alt) + { + if (wpt->altitude > maxalt) maxalt = wpt->altitude; + if (wpt->altitude < minalt) minalt = wpt->altitude; + } + } + + if (route->rte_name == NULL) + { + snprintf(buff, sizeof(buff), "Route%04d", route->rte_num); + gdb_fwrite_str(buff, -1); + } + else + gdb_fwrite_str(route->rte_name, -1); + + gdb_fwrite(&c0, 1); /* auto_name */ + + if (count == 1) gdb_fwrite(&c1, 1); /* skip max data */ + else + { + gdb_fwrite(&c0, 1); /* ??? */ + gdb_fwrite_int(GPS_Math_Deg_To_Semi(maxlat)); /* maximum latitude over route */ + gdb_fwrite_int(GPS_Math_Deg_To_Semi(maxlon)); /* maximum longitude over route */ + gdb_fwrite_alt(maxalt, unknown_alt); /* maximum altitude over route */ + + gdb_fwrite_int(GPS_Math_Deg_To_Semi(minlat)); /* minimum latitude over route */ + gdb_fwrite_int(GPS_Math_Deg_To_Semi(minlon)); /* minimum longitude over route */ + gdb_fwrite_alt(minalt, -unknown_alt); /* minimum altitude over route */ + } + + gdb_fwrite_int(count); /* number of points in route */ + + for (i = 0; i < count; i++) + { + const waypoint *wpt = list[i]; + + wpt_class = gdb_detect_rtept_class(wpt); + + if (prev != NULL) + { + gdb_fwrite_int(2); /* route link details */ + + gdb_fwrite_int(GPS_Math_Deg_To_Semi(prev->latitude)); /* ilink step 1 (end point 1) */ + gdb_fwrite_int(GPS_Math_Deg_To_Semi(prev->longitude)); + gdb_fwrite_alt(prev->altitude, unknown_alt); + + gdb_fwrite_int(GPS_Math_Deg_To_Semi(wpt->latitude)); /* ilink step 2 (end point 2) */ + gdb_fwrite_int(GPS_Math_Deg_To_Semi(wpt->longitude)); + gdb_fwrite_alt(wpt->altitude, unknown_alt); + + if (wpt->latitude > prev->latitude) /* get maximum lat, lon and alt */ + { + maxlat = wpt->latitude; + minlat = prev->latitude; + } + else + { + maxlat = prev->latitude; + minlat = wpt->latitude; + } + if (wpt->longitude > prev->longitude) + { + maxlon = wpt->longitude; + minlon = prev->longitude; + } + else + { + maxlon = prev->longitude; + minlon = wpt->longitude; + } + if (wpt->altitude != unknown_alt) + { + maxalt = wpt->altitude; + minalt = wpt->altitude; + } + else + { + maxalt = -unknown_alt; + minalt = +unknown_alt; + } + if (prev->altitude != unknown_alt) + { + if (prev->altitude > maxalt) maxalt = prev->altitude; + if (prev->altitude < minalt) minalt = prev->altitude; + } + + gdb_fwrite(&c0, 1); /* ??? */ + + gdb_fwrite_int(GPS_Math_Deg_To_Semi(maxlat)); /* maximum coords & altitude */ + gdb_fwrite_int(GPS_Math_Deg_To_Semi(maxlon)); + gdb_fwrite_alt(maxalt, unknown_alt); + + gdb_fwrite_int(GPS_Math_Deg_To_Semi(minlat)); /* minimum coords & altitude */ + gdb_fwrite_int(GPS_Math_Deg_To_Semi(minlon)); + gdb_fwrite_alt(minalt, -unknown_alt); + + if (gdb_ver > 1) + gdb_fwrite(ffbuff, 8); + } + + gdb_fwrite_str(wpt->shortname, -1); /* short name */ + + gdb_fwrite_int(wpt_class); /* class */ + gdb_fwrite_str("", -1); /* country */ + + gdb_fwrite(zbuff, 4); /* subclass part 1 */ + gdb_fwrite(ffbuff, 12); /* subclass part 2 */ + gdb_fwrite(zbuff, 2); /* subclass part 3 */ + gdb_fwrite(ffbuff, 4); /* unknown */ + + gdb_fwrite(&c0, 1); /* unknown value or string */ + gdb_fwrite(&c3, 1); /* unknown 18 bytes starting with 0x03 */ + gdb_fwrite(zbuff, 3); + gdb_fwrite(ffbuff, 4); + gdb_fwrite(zbuff, 10); + + prev = (waypoint *)wpt; + } + + gdb_fwrite_int(0); /* Zero interlink steps */ + gdb_fwrite(&c1, 1); + + if (gdb_ver > 1) + gdb_fwrite(ffbuff, 8); + + gdb_fwrite(&c0, 1); +} + +static void +gdb_write_route_cb(const route_head *route) +{ + int reclen; + size_t pos; + int count; + waypoint **list; + + list = gdb_route_point_list(route, &count); + if (count == 0) return; /* don't write empty routes */ + + gdb_fwrite_int(0); + gdb_fwrite_str("R", 1); + + pos = ftell(fout); + gdb_write_route(route, (const waypoint**)list, count); + reclen = ftell(fout) - pos; + + fseek(fout, pos - 5, SEEK_SET); + gdb_fwrite_int(reclen); + + fseek(fout, pos + reclen, SEEK_SET); + + xfree(list); +} + +/*******************************************************************************/ +/* %%% write tracks %%% */ +/*-----------------------------------------------------------------------------*/ + +void +gdb_write_track(const route_head *track) +{ + char buff[128]; + const char c0 = 0; + const char c1 = 1; + queue *elem, *tmp; + int count = track->rte_waypt_ct; + + if (track->rte_name == NULL) + snprintf(buff, sizeof(buff), "Track%04d", track->rte_num); + else + strncpy(buff, track->rte_name, sizeof(buff)); + + gdb_fwrite_str(buff, -1); + gdb_fwrite(&c0, 1); /* display */ + gdb_fwrite_int(0); /* xcolour */ + gdb_fwrite_int(count); + + QUEUE_FOR_EACH((queue *)&track->waypoint_list, elem, tmp) + { + waypoint *wpt = (waypoint *)elem; + + gdb_fwrite_int(GPS_Math_Deg_To_Semi(wpt->latitude)); + gdb_fwrite_int(GPS_Math_Deg_To_Semi(wpt->longitude)); + gdb_fwrite_alt(wpt->altitude, unknown_alt); /* altitude */ + + + if (wpt->creation_time != 0) /* creation time */ + { + gdb_fwrite(&c1, 1); + gdb_fwrite_int(wpt->creation_time); + } + else + gdb_fwrite(&c0, 1); + + gdb_fwrite_alt(wpt->depth, unknown_alt); /* depth */ + gdb_fwrite(&c0, 1); /* temperature */ + } + gdb_fwrite(&c0, 1); +} + +static void +gdb_write_track_cb(const route_head *track) /* called from track_disp_all */ +{ + int reclen; + size_t pos; + + if (track->rte_waypt_ct <= 0) return; /* don't write empty tracks */ + + gdb_fwrite_int(0); + gdb_fwrite_str("T", 1); + + pos = ftell(fout); + + gdb_write_track(track); + + reclen = ftell(fout) - pos; + fseek(fout, pos - 5, SEEK_SET); + gdb_fwrite_int(reclen); + + fseek(fout, pos + reclen, SEEK_SET); +} + +/*******************************************************************************/ + +void +gdb_write_data(void) +{ + char c1 = 1; + + gdb_hidden = route_head_alloc(); /* contains all written waypts & rtepts */ + track_add_head(gdb_hidden); /* tracks comes later and we drop this before */ + + if (doing_wpts) waypt_disp_all(gdb_write_waypt_cb); + if (doing_rtes) + { + + if (gdb_via == 0) + { + /* find out all route points we have to write as a "HIDDEN CLASS" waypoint */ + route_disp_all(NULL, NULL, gdb_write_rtewpt_cb); + } + route_disp_all(gdb_write_route_cb, NULL, NULL); + } + + track_del_head(gdb_hidden); /* vaporize our temporary queue */ + + if (doing_trks) track_disp_all(gdb_write_track_cb, NULL, NULL); + + gdb_fwrite_int(2); /* finalize gdb with empty map segment */ + gdb_fwrite_str("V", -1); + gdb_fwrite(&c1, 1); +} + +/*******************************************************************************/ + +void +gdb_init_opts(const char op) /* 1 = read; 2 = write */ +{ + gdb_via = 0; + gdb_category = 0; + gdb_ver = 2; + + if (gdb_opt_via != NULL) /* opt_via present in both ops */ + { + if ((case_ignore_strcmp(gdb_opt_via, GDB_OPT_VIA) == 0) || + (*gdb_opt_via == '\0')) + gdb_via = 1; + else + gdb_via = atoi(gdb_opt_via); + } + + if (op & 2) /* writer opts */ + { + if ((gdb_opt_category != NULL) && + (case_ignore_strcmp(gdb_opt_category, GDB_OPT_CATEGORY) != 0) && + (*gdb_opt_category != '\0')) + { + gdb_category = atoi(gdb_opt_category); + if ((gdb_category < 1) || (gdb_category > 16)) + fatal(MYNAME ": Unsupported category \"%s\"!\n", gdb_opt_category); + gdb_category = 1 << --gdb_category; + } + + gdb_ver = atoi(gdb_opt_ver); + if ((gdb_ver < GDB_VER_MIN) || (gdb_ver > GDB_VER_MAX)) + fatal(MYNAME ": Unsupported version \"%s\"!\n", gdb_opt_ver); + } +} + +/*******************************************************************************/ +/* %%% global cb's %%% */ +/*******************************************************************************/ + +static void +gdb_rd_init(const char *fname) +{ + gdb_init_opts(1); + fin_name = xstrdup(fname); fin = xfopen(fname, "rb", MYNAME); - gdb_hidden = route_head_alloc(); - track_add_head(gdb_hidden); } -static void gdb_rd_deinit(void) +static void +gdb_wr_init(const char *fname) +{ + gdb_init_opts(2); + + fout_name = xstrdup(fname); + fout = xfopen(fname, "wb", MYNAME); +} + +static void +gdb_rd_deinit(void) { - track_del_head(gdb_hidden); fclose(fin); xfree(fin_name); + fin_name = NULL; +} + +static void +gdb_wr_deinit(void) +{ + fclose(fout); + xfree(fout_name); + fout_name = NULL; } -static void gdb_read(void) +static void +gdb_read(void) { gdb_read_file_header(); gdb_read_data(); } +static void +gdb_write(void) +{ + gdb_write_file_header(NULL); + gdb_write_data(); +} + +/*******************************************************************************/ ff_vecs_t gdb_vecs = { ff_type_file, - { ff_cap_read, ff_cap_read, ff_cap_read }, /* FF_CAP_RW_ALL, !!! I hope !!! */ + FF_CAP_RW_ALL, gdb_rd_init, - NULL, /* gdb_wr_init, */ + gdb_wr_init, gdb_rd_deinit, - NULL, /* gdb_wr_deinit, */ + gdb_wr_deinit, gdb_read, - NULL, /* gdb_write, */ + gdb_write, NULL, - NULL /* gdb_args */ + gdb_args }; + +/*******************************************************************************/ diff --git a/gpsbabel/reference/gdb-sample.gpx b/gpsbabel/reference/gdb-sample.gpx index 06e1f726b..b848b7742 100644 --- a/gpsbabel/reference/gdb-sample.gpx +++ b/gpsbabel/reference/gdb-sample.gpx @@ -79,16 +79,26 @@ xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/ Residence + 416 + Fahren Sie auf die Luis-Ferdinand-Schönherr-Strasse nach Norden + Waypoint + 417 + Waypoint + 418 + Biegen Sie rechts ab auf die Liebknechtstrasse + Waypoint + 419 + Waypoint @@ -98,13 +108,21 @@ xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/ Golf Course + 420 + Fahren Sie auf die Liebknechtstrasse nach Südosten + Waypoint + 421 + Biegen Sie links ab auf die Jahnstrasse + Waypoint + 422 + Waypoint @@ -114,112 +132,195 @@ xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/ Golf Course + 423 + Fahren Sie auf die Jahnstrasse nach Norden + Waypoint + 424 + Biegen Sie links ab auf die Neundorfer Strasse + Waypoint + 425 + Biegen Sie rechts ab auf die Scharnhorststrasse + Waypoint + 426 + Biegen Sie rechts ab auf die Schminckestrasse + Waypoint + 427 + Biegen Sie links ab auf die Kopernikusstrasse + Waypoint + 428 + Waypoint + 429 + Waypoint + 430 + Waypoint + 431 + Waypoint + 432 + Ordnen Sie sich rechts ein in Richtung Talstrasse + Waypoint + 433 + Waypoint + 434 + Biegen Sie rechts ab auf die Zwoschwitzer Strasse + Waypoint + 435 + Waypoint + 436 + Waypoint + 437 + Waypoint + 438 + Waypoint + 439 + Waypoint + 440 + Waypoint + 441 + Waypoint + 442 + Waypoint + 443 + Waypoint + 444 + Biegen Sie links ab auf die An Der Schöpsdrehe + Waypoint + 445 + Waypoint + 446 + Waypoint + 447 + Biegen Sie rechts ab auf die Elsterberger Strasse + Waypoint + 448 + Waypoint + 449 + Waypoint + 450 + Waypoint + 451 + Waypoint + 452 + Waypoint + 453 + Waypoint + 454 + Biegen Sie rechts ab auf die Robert-Schenker-Strasse + Waypoint + 455 + Biegen Sie links ab auf die Rosa-Luxemburg-Strasse + Waypoint + 456 + Waypoint + 457 + Waypoint + 458 + Waypoint @@ -229,61 +330,104 @@ xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/ Golf Course + 459 + Fahren Sie auf die Piehlerstrasse nach Nordwesten + Waypoint + 460 + Biegen Sie rechts ab auf die Greizer Strasse + Waypoint + 461 + Waypoint + 462 + Waypoint + 463 + Waypoint + 464 + Waypoint + 465 + Waypoint + 466 + Waypoint + 467 + Waypoint + 468 + Waypoint + 469 + Waypoint + 470 + Halten Sie sich rechts in Richtung Carolinenstrasse + Waypoint + 471 + Biegen Sie rechts ab auf die Gartenweg + Waypoint + 472 + Biegen Sie links ab auf die B94 + Waypoint + 473 + Waypoint + 474 + Waypoint + 475 + Waypoint + 476 + Waypoint + 477 + Waypoint @@ -293,142 +437,245 @@ xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/ Golf Course + 478 + Fahren Sie auf die August-Bebel-Strasse nach Südosten + Waypoint + 479 + Waypoint + 480 + Waypoint + 481 + Biegen Sie links ab auf die Werdauer Strasse + Waypoint + 482 + Waypoint + 483 + Waypoint + 484 + Halten Sie sich rechts in Richtung Werdauer Strasse + Waypoint + 485 + Waypoint + 486 + Waypoint + 487 + Waypoint + 488 + Waypoint + 489 + Waypoint + 490 + Waypoint + 491 + Waypoint + 492 + Waypoint + 493 + Waypoint + 494 + Waypoint + 495 + Waypoint + 496 + Waypoint + 497 + Waypoint + 498 + Waypoint + 499 + Waypoint + 500 + Waypoint + 501 + Waypoint + 502 + Waypoint + 503 + Halten Sie sich links in Richtung Marienstrasse + Waypoint + 504 + Biegen Sie links ab auf die Uferstrasse + Waypoint + 505 + Waypoint + 506 + Waypoint + 507 + Biegen Sie links ab auf die Querstrasse + Waypoint + 508 + Biegen Sie rechts ab auf die Carthäuserstrasse + Waypoint + 509 + Waypoint + 510 + Waypoint + 511 + Waypoint + 512 + Waypoint + 513 + Halten Sie sich links in Richtung S54 + Waypoint + 514 + Waypoint + 515 + Waypoint + 516 + Waypoint + 517 + Biegen Sie rechts ab auf die Leipziger Strasse + Waypoint + 518 + Biegen Sie rechts ab auf die Leipziger Strasse + Waypoint + 519 + Waypoint + 520 + Biegen Sie links ab auf die Ponitzer Strasse + Waypoint + 521 + Waypoint + 522 + Waypoint + 523 + Waypoint @@ -438,34 +685,57 @@ xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/ Golf Course + 524 + Fahren Sie auf die Gosel nach Nordosten + Waypoint + 525 + Waypoint + 526 + Waypoint + 527 + Waypoint + 528 + Biegen Sie rechts ab auf die Gössnitzer Strasse + Waypoint + 529 + Waypoint + 530 + Waypoint + 531 + Biegen Sie links ab auf die Altenburger Strasse + Waypoint + 532 + Waypoint + 533 + Waypoint 3 @@ -474,76 +744,129 @@ xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/ Waypoint + 534 + Fahren Sie auf die Altenburger Strasse nach Norden + Waypoint + 535 + Waypoint + 536 + Waypoint + 537 + Waypoint + 538 + Waypoint + 539 + Waypoint + 540 + Waypoint + 541 + Waypoint + 542 + Waypoint + 543 + Waypoint + 544 + Waypoint + 545 + Waypoint + 546 + Waypoint + 547 + Waypoint + 548 + Waypoint + 549 + Biegen Sie links ab auf die Hauptstrasse + Waypoint + 550 + Biegen Sie rechts ab auf die K61 + Waypoint + 551 + Waypoint + 552 + Waypoint + 553 + Waypoint + 554 + Waypoint + 555 + Biegen Sie rechts ab auf die Schmöllner Strasse + Waypoint + 556 + Halten Sie sich rechts in Richtung Straße + Waypoint + 557 + Waypoint @@ -553,148 +876,251 @@ xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/ Golf Course + 558 + Fahren Sie auf die Straße nach Nordosten + Waypoint + 559 + Waypoint + 560 + Waypoint + 561 + Waypoint + 562 + Waypoint + 563 + Waypoint + 564 + Waypoint + 565 + Waypoint + 566 + Waypoint + 567 + Waypoint + 568 + Waypoint + 569 + Waypoint + 570 + Waypoint + 571 + Waypoint + 572 + Waypoint + 573 + Waypoint + 574 + Waypoint + 575 + Waypoint + 576 + Waypoint + 577 + Waypoint + 578 + Waypoint + 579 + Waypoint + 580 + Waypoint + 581 + Waypoint + 582 + Waypoint + 583 + Waypoint + 584 + Waypoint + 585 + Waypoint + 586 + Waypoint + 587 + Waypoint + 588 + Biegen Sie links ab auf die Leipziger Strasse + Waypoint + 589 + Waypoint + 590 + Waypoint + 591 + Waypoint + 592 + Waypoint + 593 + Waypoint + 594 + Waypoint + 595 + Waypoint + 596 + Fahren Sie rechts ab auf die Prinz-Eugen-Strasse + Waypoint + 597 + Waypoint + 598 + Waypoint + 599 + Biegen Sie rechts ab auf die Wolfgang-Heinze-Strasse + Waypoint + 600 + Biegen Sie rechts ab auf die Meusdorfer Strasse + Waypoint + 601 + Biegen Sie rechts ab auf die Arno-Nitzsche-Strasse + Waypoint + 602 + Waypoint + 603 + Waypoint + 604 + Ordnen Sie sich rechts ein in Richtung Strasse Des 18. Oktober + Waypoint + 605 + Waypoint diff --git a/gpsbabel/testo b/gpsbabel/testo index 8c5db2a43..f4495f110 100755 --- a/gpsbabel/testo +++ b/gpsbabel/testo @@ -715,11 +715,17 @@ ${PNAME} -t -i pathaway -f reference/track/pathaway.pdb -o gpx -F ${TMPDIR}/path compare ${TMPDIR}/pathaway.gpx reference/track/pathaway.gpx # -# Garmin GPS Database .gdb read test +# Garmin GPS Database .gdb tests # rm -f ${TMPDIR}/gdb-* ${PNAME} -w -r -t -i gdb -f reference/gdb-sample.gdb -o gpx -F ${TMPDIR}/gdb-sample.gpx compare reference/gdb-sample.gpx ${TMPDIR}/gdb-sample.gpx +${PNAME} -w -r -t -i gpx -f reference/gdb-sample.gpx -o gdb,ver=1 -F ${TMPDIR}/gdb-sample.gdb +${PNAME} -w -r -t -i gdb -f ${TMPDIR}/gdb-sample.gdb -o gpx -F ${TMPDIR}/gdb-sample.gpx +# +# Because of Garmin coordinates storage gpx is not good for this test +# compare reference/gdb-sample.gpx ${TMPDIR}/gdb-sample.gpx +# # # Vito Navigator II .smt tests -- 2.30.2